import numpy as np
from sklearn.metrics.pairwise import cosine_similarity


class ContentBasedRecommender:
    def __init__(self, items_catalog):
        """
        初始化基于内容的推荐器

        参数:
            items_catalog: 所有服装项的列表
        """
        self.items_catalog = items_catalog

        # 按类别组织服装项，便于快速访问
        self.items_by_category = {}
        for item in items_catalog:
            if item.category not in self.items_by_category:
                self.items_by_category[item.category] = []
            self.items_by_category[item.category].append(item)

        # 提取所有可能的属性值，用于特征向量构建
        self.all_attributes = self._extract_all_attributes()

    def _extract_all_attributes(self):
        """提取所有可能的属性值"""
        attributes = {
            "category": set(),
            "subcategory": set(),
            "color": set(),
            "material": set(),
            "season": set(),
            "fit": set()
        }

        for item in self.items_catalog:
            attributes["category"].add(item.category)
            attributes["subcategory"].add(item.subcategory)
            attributes["color"].add(item.color)
            attributes["material"].add(item.material)
            attributes["season"].add(item.season)
            attributes["fit"].add(item.fit)

        # 转换为排序后的列表，确保特征向量的一致性
        return {k: sorted([item for item in list(v) if item is not None]) for k, v in attributes.items()}

    def _create_feature_vector(self, item):
        """为服装项创建特征向量"""
        # 初始化特征向量
        feature_vector = []

        # 为每个属性类别添加one-hot编码
        for attr_type, attr_values in self.all_attributes.items():
            item_value = getattr(item, attr_type)
            for value in attr_values:
                feature_vector.append(1.0 if value == item_value else 0.0)

        return np.array(feature_vector)

    def recommend_items(self, user_profile, category, context=None, top_n=5):
        """
        为用户推荐特定类别的服装项

        参数:
            user_profile: 用户档案
            category: 要推荐的服装类别
            context: 上下文信息 (可选)
            top_n: 推荐数量

        返回:
            推荐的服装项列表
        """
        if category not in self.items_by_category:
            return []

        category_items = self.items_by_category[category]

        # 如果用户没有交互历史，返回基于上下文的推荐
        if not user_profile.liked_items:
            return self._recommend_by_context(category_items, context, top_n)

        # 计算用户对每个属性的偏好
        user_preferences = user_profile.get_preference_vector(self.all_attributes)

        # 计算每个服装项的特征向量
        item_features = [self._create_feature_vector(item) for item in category_items]

        # 计算相似度
        if len(user_preferences) > 0 and len(item_features) > 0:
            similarities = cosine_similarity([user_preferences], item_features)[0]
        else:
            similarities = np.zeros(len(category_items))

        # 如果有上下文信息，考虑上下文相关性
        if context:
            context_scores = self._calculate_context_relevance(category_items, context)
            # 结合用户偏好和上下文相关性
            combined_scores = 0.7 * similarities + 0.3 * context_scores
        else:
            combined_scores = similarities

        # 获取得分最高的项
        top_indices = np.argsort(combined_scores)[-top_n:][::-1]
        return [category_items[i] for i in top_indices]

    def _recommend_by_context(self, items, context, top_n=5):
        """基于上下文信息推荐服装项"""
        if not context:
            # 如果没有上下文，随机返回
            if len(items) <= top_n:
                return items
            return np.random.choice(items, top_n, replace=False)

        # 计算每个项的上下文相关性
        context_scores = self._calculate_context_relevance(items, context)

        # 获取得分最高的项
        top_indices = np.argsort(context_scores)[-top_n:][::-1]
        return [items[i] for i in top_indices]

    def _calculate_context_relevance(self, items, context):
        """计算服装项与上下文的相关性"""
        scores = np.zeros(len(items))

        if not context:
            return scores

        # 获取当前季节
        current_season = context.get_season()

        for i, item in enumerate(items):
            score = 0.0

            # 季节匹配度
            if item.season == current_season:
                score += 1.0
            elif item.season in ["四季", "春秋", "秋冬", "春夏"]:
                if current_season in item.season or "四季" in item.season:
                    score += 0.7

            # 天气适应性
            if context.weather:
                if context.weather == "雨天" and item.material not in ["皮革", "纸", "丝绸"]:
                    score += 0.5
                elif context.weather == "雪天" and item.category in ["外套", "靴子"]:
                    score += 0.5

            # 风格主题匹配
            if context.style_theme:
                style_subcategory_map = {
                    "休闲": ["短袖", "卫衣", "牛仔裤", "运动裤", "运动鞋"],
                    "正式": ["衬衣", "西裤", "皮鞋","高跟鞋"],
                    "运动": ["短袖", "运动裤", "运动鞋"],
                    "复古": ["衬衣", "牛仔裤"],
                    "商务": ["衬衣", "西裤", "皮鞋"]
                }

                if context.style_theme in style_subcategory_map and item.subcategory in style_subcategory_map[
                    context.style_theme]:
                    score += 0.8

            scores[i] = score

        # 归一化分数
        if np.max(scores) > 0:
            scores = scores / np.max(scores)

        return scores

    def find_similar_items(self, item, category=None, top_n=5):
        """查找与给定服装项相似的项"""
        # 如果指定了类别，只在该类别中查找
        if category:
            candidate_items = self.items_by_category.get(category, [])
        else:
            candidate_items = self.items_catalog

        # 排除自身
        candidate_items = [i for i in candidate_items if i.clothes_id != item.clothes_id]

        if not candidate_items:
            return []

        # 计算特征向量
        item_feature = self._create_feature_vector(item)
        candidate_features = [self._create_feature_vector(i) for i in candidate_items]

        # 计算相似度
        similarities = cosine_similarity([item_feature], candidate_features)[0]

        # 获取最相似的项
        top_indices = np.argsort(similarities)[-top_n:][::-1]
        return [candidate_items[i] for i in top_indices]
